﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;

namespace QuadRemote.Core
{
    public class LogValueContainer : List<LogValue>
    {
        public String name;
        public byte id;
        public bool isSigned;
        public int numValues;
        public int numBytes;
        public int intBits;
        public int floatBits;

        public int minTimestamp = int.MaxValue;
        public int maxTimestamp = 0;

        public LogValueContainer(StorageReader fromFile)
        {
            StorageMarker marker = fromFile.readMarker();
            while (marker != StorageMarker.LIST_END)
            {
                String listName = fromFile.ReadString();
                Console.WriteLine("\t\t" + (int)marker + " => " + listName);
                if (listName == "name") { name = fromFile.ReadString(); Console.WriteLine("\t\tName = " + name); }
                else if (listName == "id") { id = (byte)fromFile.ReadInt32(); Console.WriteLine("\t\tID = " + (int)id); }
                else if (listName == "numValues") numValues = fromFile.ReadInt32();
                else if (listName == "numBytes") numBytes = fromFile.ReadInt32();
                else if (listName == "intBits") intBits = fromFile.ReadInt32();
                else if (listName == "floatBits") floatBits = fromFile.ReadInt32();
                else if (listName == "isSigned") isSigned = fromFile.ReadBoolean();
                else if (listName == "data")
                {
                    int length = fromFile.ReadInt32();

                    while (length > 0)
                    {
                        int timestamp = fromFile.ReadInt32();
                        float[] vars = new float[numValues];
                        for (int i = 0; i < numValues; i++)
                            vars[i] = fromFile.ReadSingle();

                        Add(new LogValue(timestamp, vars));
                        length -= 4 + numValues * 4;
                    }
                }
                else fromFile.ignore(marker);
                
                marker = fromFile.readMarker();
            }

            if (Count > 0)
            {
                minTimestamp = this[0].timestamp;
                maxTimestamp = this[Count - 1].timestamp;
            }
        }

        public LogValueContainer(byte id, String name)
        {
            int i;
            this.name = name;
            this.id = id;
            minTimestamp = maxTimestamp = 0;

            /*
            countMax = 0;
            countTest = 0;
            k = 0;
            DateTime debugTime = DateTime.Now;

            for (i = minTimestamp - 3; i < maxTimestamp + 2; i++)
            {
                k++;
                findTimestamp(i);
            }
            float avg = (float)countTest / k;
            Console.WriteLine("Average1: " + k + " runs => " + avg + " max = " + countMax + "  ms = " + (DateTime.Now - debugTime).TotalMilliseconds);
            countMax = 0;
            countTest = 0;
            k = 0;
            debugTime = DateTime.Now;

            for (i = minTimestamp - 3; i < maxTimestamp + 2; i++)
            {
                k++;
                findTimestamp2(i);
            }
            avg = (float)countTest / k;
            Console.WriteLine("Average2: " + k + " runs => " + avg + " max = " + countMax + "  ms = " + (DateTime.Now - debugTime).TotalMilliseconds);*/
        }

        public void save(StorageWriter writer)
        {
            int count = Count;

            writer.startList("LogValueContainer");
            writer.listString("name", name);
            writer.listInt("id", (int)id);
            writer.listInt("numValues", (int)numValues);
            writer.listInt("numBytes", (int)numBytes);
            writer.listInt("intBits", (int)numBytes);
            writer.listInt("floatBits", (int)numBytes);
            writer.listBool("isSigned",isSigned);
            writer.listByteArray("data", (4 + 4 * numValues)*count);

            for (int i=0;i<count;i++)
            {
                writer.Write(this[i].timestamp);
                for (int j = 0; j < numValues;j++)
                    writer.Write(this[i].payload[j]);
            }
            writer.endList();
        }

        /**
         * return index of first logvalue with timestamp >= param
         */
        public int findTimestamp(int t)
        {
            DateTime debugTime = DateTime.Now;

            if (t <= minTimestamp) return 0;
            if (t >= maxTimestamp) return Count - 1;

            int min = 0;
            int max = Count - 1;

            if (Count == 1) return 0;

            do
            {
                int minT = this[min].timestamp;
                int maxT = this[max].timestamp;

                if (minT >= t && min > 0 && this[min - 1].timestamp < t) return min;
                if (maxT >= t && max > 0 && this[max - 1].timestamp < t) return max;

                float est = (float)(maxT - minT) / (float)(max - min);
                int est2 = t - minT;

                est2 = (int)(est2 / est) + min;
                if (est2 < 0) est2 = 0;

                if (est2 >= Count) est2 = Count - 1;
                int estT = this[est2].timestamp;
                if (estT >= t && est2 > 0 && this[est2 - 1].timestamp < t) { return est2; }

                if (estT > t)
                {
                    max = est2 - 1;
                    min++;
                }
                else if (estT < t)
                {
                    min = est2 + 1;
                    max--;

                }
            } while (true);

            return 0;
        }

        public void parseIncoming(int timestamp, byte[] payload, int offset)
        {
            int i;
            float[] vals = new float[numValues];

            //Console.Write("Log: " + timestamp + " = ");
            for (i = 0; i < numValues; i++)
            {
                vals[i] = getValue(i * (intBits + floatBits) / 8 + offset, payload);
            //    Console.Write(vals[i] + " , ");
            }
            //Console.WriteLine();

            if (timestamp > maxTimestamp)
            {
                Add(new LogValue(timestamp, vals));
                maxTimestamp = timestamp;
            }
            else
            {
                int index = findTimestamp(timestamp);
                if (this[index].timestamp == timestamp)
                {
                    this[index] = new LogValue(timestamp, vals);
                }
                else
                {
                    Insert(index, new LogValue(timestamp, vals));
                }
            }

            if (timestamp < minTimestamp || minTimestamp == 0) minTimestamp = timestamp;
        }

        public float getValue(int offset, byte[] payload)
        {
            Int64 uv = 0;

            int i;
            for (i = 0; i < (intBits+floatBits)/8; i++)
            {
                uv += (Int64)((Int64)payload[i+offset] << ((i * 8)));
            }

            if (isSigned && (payload[offset + (intBits+floatBits)/8 - 1] & 0x80) == 0x80)
            {
                uv = -((Int64)Math.Pow(2, intBits+floatBits) - uv);
            }

            double uf = (double)uv / Math.Pow(2,floatBits);

            // smallest float val
            double floatSig = 1.0 / Math.Pow(2,floatBits);

            int sig = 0;
            while (floatSig < 10)
            {
                sig++;
                floatSig *= 10;
            }

            uf = Math.Round(uf, sig);

            return (float)uf;
        }

        public String getName(int index)
        {
            String n = name;
            if (n.Contains("[") && n.Contains("]"))
            {
                String orgName = n;
                n = n.Substring(0, n.IndexOf("["));
                String vars = orgName.Substring(orgName.IndexOf("[") + 1, orgName.IndexOf("]") - orgName.IndexOf("[") - 1);
                string[] varsArr = vars.Split(new string[] { "," }, StringSplitOptions.None);
                n += "[" + varsArr[index] + "]";
            }

            return n;
        }
    }
}
